

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/slab.h>


#define OK					(0)
#define ERROR				(-1)
#define GLOBALMEM_SIZE		(1024)
#define MEM_CLEAR			_IO(0x01, 0)
#define GLOBALMEM_MAJOR		(232)


static int globalmem_major = GLOBALMEM_MAJOR;

struct globalmem_dev {
	struct cdev 	cdev;
	unsigned char 	mem[GLOBALMEM_SIZE];
};

static struct globalmem_dev 	globalmem_dev;

unsigned int sub_dev_num;
struct cdev *g_dev;


static int globalmem_open(struct inode *p, struct file *filp)
{
	struct globalmem_dev *dev;

	dev = container_of(p->i_cdev, struct globalmem_dev, cdev);
	filp->private_data = dev;

	return 0;
}


static ssize_t globalmem_read(struct file *filp, char __user *buff, size_t count, loff_t *ppos)
{
	int ret = 0;
	unsigned long p = *ppos;
	struct globalmem_dev *dev = filp->private_data;

	if (p >= GLOBALMEM_SIZE)
		return 0;

	if (count > GLOBALMEM_SIZE - p)
		count = GLOBALMEM_SIZE - p;

	if (copy_to_user(buff, (void *)(dev->mem + p), count))
		ret = -EFAULT;
	else {
		*ppos += count;
		ret = count;

		printk(KERN_INFO "read %d bytes from %d\n", count, (int)p);
	}

	return ret;
}


static ssize_t globalmem_write(struct file *filp, const char __user *buff, size_t count, loff_t *ppos)
{
	int ret = 0;
	unsigned long p = *ppos;
	struct globalmem_dev *dev = filp->private_data;

	if (p >= GLOBALMEM_SIZE)
		return 0;

	if (count > GLOBALMEM_SIZE - p)
		count = GLOBALMEM_SIZE - p;

	if (copy_from_user(dev->mem + p, buff, count))
		ret = -EFAULT;
	else {
		*ppos += count;
		ret = count;
		printk(KERN_INFO "written %d bytes to %d\n", count, (int)p);
	}

	return ret;
}


static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)
{
	loff_t ret;
	//struct globalmem_dev *dev = filp->private_data;

	switch (orig) {
	case 0:
		if (offset < 0) {
			ret = -EINVAL;
			break;
		}

		if ((unsigned int)offset > GLOBALMEM_SIZE) {
			ret = -EINVAL;
			break;
		}

		filp->f_pos = (unsigned int)offset;
		ret = filp->f_pos;
		break;
	case 1:
		if (filp->f_pos + offset > GLOBALMEM_SIZE) {
			ret = -EINVAL;
			break;
		}

		if (filp->f_pos + offset < 0) {
			ret = -EINVAL;
			break;
		}
		filp->f_pos += offset;
		ret = filp->f_pos;
		break;
	default:
		ret = -EINVAL;
	}

	return ret;
}


static long
globalmem_itol(struct file *filp, unsigned int cmd, unsigned long argv)
{
	struct globalmem_dev *dev = filp->private_data;
	
	switch (cmd) {
	case MEM_CLEAR:
		memset(dev->mem, 0, GLOBALMEM_SIZE);
		printk(KERN_INFO "globalmem is set to 0\n");
		break;
	default:
		return -EINVAL;
	}

	return OK;
}




static const struct file_operations globalmem_fops = {
	.owner = THIS_MODULE,
	.read = globalmem_read,
	.write = globalmem_write,
	.llseek = globalmem_llseek,
	.open = globalmem_open,
	.unlocked_ioctl = globalmem_itol,
};


static int globalmem_cdev_init(struct globalmem_dev *gdev)
{
	int ret = 0;
	int dev_num = 0;

	dev_num = MKDEV(globalmem_major, 0);

	cdev_init(&gdev->cdev, &globalmem_fops);
	gdev->cdev.owner = THIS_MODULE;

	ret = cdev_add(&gdev->cdev, dev_num, 1);
	if (ret) {
		printk(KERN_ERR "Error %d adding globalmem", ret);
		return ERROR;
	}

	return OK;
}

static int __init char_dev_init(void)
{
	dev_t dev_num;
	int ret = 0;
	
	dev_num = MKDEV(globalmem_major, 0);
	
	printk(KERN_INFO "dev num is %d\n", dev_num);

	if (globalmem_major)
		ret = register_chrdev_region(dev_num, 1, "globalmem");
	else{
		ret = alloc_chrdev_region(&dev_num, 0, 1, "globalmem");
		globalmem_major = MAJOR(dev_num);
	}

	if (ret < 0) {
		printk(KERN_ERR "globalmem device register failed\n");
		return ERROR;
	}

	globalmem_cdev_init(&globalmem_dev);
	return 0;
}


static void __exit char_dev_exit(void)
{
	cdev_del(&globalmem_dev.cdev);
	unregister_chrdev_region(MKDEV(globalmem_major, 0), 1);
	
	printk(KERN_INFO "char dev is dead.\n");
}


module_init(char_dev_init);
module_exit(char_dev_exit);
MODULE_LICENSE("GPL");


